import 'dart:async';

import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';
import 'package:xbaistack/utils/helper.dart';
import 'package:xbaistack/widget/state_view.dart';

class NetWorkListener extends StatefulWidget {
  /// 用于构建真正的视图内容
  final WidgetBuilder builder;

  const NetWorkListener({super.key, required this.builder});

  @override
  State<StatefulWidget> createState() => _NetWorkListenerState();
}

class _NetWorkListenerState extends State<NetWorkListener> {
  /// 首次注册监听器的时候，监听器和检查方法都会执行一次。
  /// 因此使用变量来过滤首次监听器的返回结果，
  /// 仅当初始化后，网络发生变更再执行监听器回调逻辑；
  bool isLoadFirst = true;

  /// 用于保存上一次注册监听器的结果，
  /// 放置监听被重复注册浪费性能，这样能保证多次执行的时候仅注册一个有效监听器；
  StreamSubscription<List<ConnectivityResult>>? subscription;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: Connectivity().checkConnectivity(), // 首次检查
      builder: (ctx, snapshot) {
        // 如果当前连接状态是等待中，则直接返回一个空占位视图
        final state = snapshot.connectionState;
        if (state == ConnectionState.waiting) {
          return const SizedBox.shrink();
        }

        // 异步加载完成后
        // snapshot.data 就指向了 Connectivity().checkConnectivity() 的返回值
        // 因此 snapshot.data = List<ConnectivityResult>；

        // 判断当前网络是否可用（目前仅判断 mobile/wifi 两种）
        bool isNetWorkAvailable = Helper.contains(snapshot.data, [
          ConnectivityResult.mobile,
          ConnectivityResult.wifi,
        ]);

        // 网络可见的状态下直接返回用户视图
        if (isNetWorkAvailable) {
          return widget.builder.call(context);
        }

        // 当前网络不可用，我们需要监听当前网络状态
        // "??=" 代表仅首次为空时才会进行初始化，否则不执行后面的逻辑；
        subscription ??= Connectivity().onConnectivityChanged.listen((results) {
          if (!isLoadFirst) {
            // 网络变更后及时判断当前网络的状态
            // 这和上面的逻辑差不多
            isNetWorkAvailable = Helper.contains(results, [
              ConnectivityResult.mobile,
              ConnectivityResult.wifi,
            ]);

            // 如果当前有网络，则刷新页面，否则不做任何处理
            // 这里网络变得真正后，代表我们已经渲染出用户视图了
            // 所以，监听器用了一次后我们就可以将其注销了
            if (isNetWorkAvailable) {
              setState(() {});
              subscription?.cancel();
              subscription = null;
            }
          } else {
            // 因为首次由 Connectivity().checkConnectivity() 进行网络检查了
            // 所以，在无网的情况下，监听不需要再次执行
            // 只需要当网络变更的时候执行就可以了
            isLoadFirst = false;
          }
        });

        // 返回网络异常信息提示
        return StatusView(
          status: Status.NETWORK_ERROR,
          retry: () => setState(() {}),
        );

        // 显示一个网络异常的状态视图，并且支持重试
        // StatusView 是前面「状态视图加载器」分享的内容，如果你没看过的话
        // 直接用上面的或者你自己的方式去展示都是可以的；
        // return StatusView(
        //   status: Status.NETWORK_ERROR,
        //   retry: () => setState(() {}),
        // );
      },
    );
  }

  @override
  void dispose() {
    super.dispose();
    subscription?.cancel();
    subscription = null;
  }
}
